#include "helper.h"
#include "world.h"
#include "physics.h"
#include "model/planet.h"
#include "model/skyBox.h"
#include "model/sun.h"
#include "camera.h"
#include "control.h"
#include "config.hpp"
#include <memory>
#include <windows.h>
#include <iostream>

void set_tempreture(std::shared_ptr<Sun> sun, std::shared_ptr<Camera> camera)
{
	constexpr float ratio = 300;
	float d = glm::distance(sun->getPosition(), camera->getPosition());
	sun->set_tempreture(ratio * d);
}

std::unique_ptr<Config> init_config()
{
    auto config = std::make_unique<Config>();
    config->add_config<glm::vec3>("sun_A_velocity");
    config->add_config<glm::vec3>("sun_B_velocity");
    config->add_config<glm::vec3>("sun_C_velocity");
    config->add_config<float>("sun_A_mass");
    config->add_config<float>("sun_B_mass");
    config->add_config<float>("sun_C_mass");
    config->add_config<float>("planet_mass");
    config->add_config<float>("sun_A_radius");
    config->add_config<float>("sun_B_radius");
    config->add_config<float>("sun_C_radius");
    config->add_config<float>("planet_radius");
    config->add_config<glm::vec3>("sun_A_position");
    config->add_config<glm::vec3>("sun_B_position");
    config->add_config<glm::vec3>("sun_C_position");
    config->load_config("config.txt");
    return config;
}

int main()
{
	typedef std::shared_ptr<Model> ModelPtr;
	typedef std::shared_ptr<Physictype> PhysictypePtr;
	auto config = init_config();
	if(!config->is_all_loaded())
	{
		// 阻止窗口关闭
		while(true) Sleep(1000);
	}
	GLFWwindow *window = createBasicWindow("3BodyViewer");

	auto camera = std::shared_ptr<Camera>(new Camera);
	// 初始化模型类
	float r1, r2, r3, r4;
	config->get_config<float>(r1, "sun_A_radius");
	config->get_config<float>(r2, "sun_B_radius");
	config->get_config<float>(r3, "sun_C_radius");
	config->get_config<float>(r4, "planet_radius");
	auto solar_texture_id = loadTexture("../texture/solar.jpg");
	auto sun_A = std::make_shared<Sun>(solar_texture_id, r1);
	auto sun_B = std::make_shared<Sun>(solar_texture_id, r2);
	auto sun_C = std::make_shared<Sun>(solar_texture_id, r3);
	auto planet = std::make_shared<Planet>("../texture/planet.jpg", r4);
	auto skybox = std::make_shared<SkyBox>("../texture/background.jpg", 1);

	// 初始化控制类
	auto control = Control(window, camera, planet, r4 * 1.15);

	// 初始化物理类
	// 将模型绑定到对应的物理类上
	auto sun_A_phy = std::make_shared<Physictype>(sun_A);
	auto sun_B_phy = std::make_shared<Physictype>(sun_B);
	auto sun_C_phy = std::make_shared<Physictype>(sun_C);
	auto planet_phy = std::make_shared<Physictype>(planet);

	// 物理系统
	auto crushcheck = std::shared_ptr<CrushCheck>(new CrushCheck());
	auto gravity = std::shared_ptr<Gravity>(new Gravity());
	// 设置初始位置
	glm::vec3 pos1, pos2, pos3;
	config->get_config<glm::vec3>(pos1, "sun_A_position");
	config->get_config<glm::vec3>(pos2, "sun_B_position");
	config->get_config<glm::vec3>(pos3, "sun_C_position");
	sun_A->setPosition(pos1);
	sun_B->setPosition(pos2);
	sun_C->setPosition(pos3);
	// 设置初始速度
	glm::vec3 v1, v2, v3;
	config->get_config<glm::vec3>(v1, "sun_A_velocity");
	config->get_config<glm::vec3>(v2, "sun_B_velocity");
	config->get_config<glm::vec3>(v3, "sun_C_velocity");
	sun_A_phy->setVelocity(v1);
	sun_B_phy->setVelocity(v2);
	sun_C_phy->setVelocity(v3);
	float m1, m2, m3, m4;
	config->get_config<float>(m1, "sun_A_mass");
	config->get_config<float>(m2, "sun_B_mass");
	config->get_config<float>(m3, "sun_C_mass");
	config->get_config<float>(m4, "planet_mass");
	sun_A_phy->setMass(m1);
	sun_B_phy->setMass(m2);
	sun_C_phy->setMass(m3);
	planet_phy->setMass(m4);
	sun_A_phy->setRadius(r1);
	sun_B_phy->setRadius(r2);
	sun_C_phy->setRadius(r3);
	planet_phy->setRadius(r4);
	// 控制受到重力作用和加入碰撞检测的对象
	gravity.get()->get_in_gravity(std::vector<PhysictypePtr>({sun_A_phy, sun_B_phy, sun_C_phy, planet_phy}));
	crushcheck.get()->get_in_crushcheck(std::vector<PhysictypePtr>({sun_A_phy, sun_B_phy, sun_C_phy, planet_phy}));
	// 加入渲染类
	World world;
	world.addModel(std::vector<std::shared_ptr<Model>>({sun_A, sun_B, sun_C, planet, skybox}));
	world.setCamera(camera);
	glEnable(GL_DEPTH_TEST);
	// 初始化相机位置
	camera->setPosition(glm::vec3(0, 0, 15.0f));
	camera->aimAt(glm::vec3(0, 0, 0));
	while (!glfwWindowShouldClose(window))
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		gravity.get()->update_acc();
		gravity.get()->update_position();
		gravity.get()->update_velocity();
		crushcheck->update_crush();
		control.update();
		set_tempreture(sun_A, camera);
		set_tempreture(sun_B, camera);
		set_tempreture(sun_C, camera);
		world.render();

		glfwSwapBuffers(window);
		glfwPollEvents();
		Sleep(5);
	}
	return 0;
}